/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simhya.simengine.hybrid;
import org.apache.commons.math.ode.DerivativeException;
import simhya.model.flat.FlatModel;
import simhya.matheval.SymbolArray;
import java.util.HashSet;

/**
 *
 * @author luca
 */
public class GuardedHybridOdeFunction extends HybridOdeFunction {
    SymbolArray vars;
    boolean[] enabled;
    HashSet<Integer> evaluatedTransitions;

    
    public GuardedHybridOdeFunction(FlatModel model, boolean allSwitchable) {
        super(model,allSwitchable);
        vars = SymbolArray.getNewFastEvalSymbolArray(this.dimension);
        enabled = new boolean[model.getNumberOfTransitions()];
        for (int i=0;i<this.numberOfContinuousTransitions;i++) {
            int j = this.continuousTransitions[i];
            enabled[j] = guard[j].evaluate() || guard[j].changeContinuously();
        }
        for (int i=0;i<this.numberOfHybridTransitions;i++) {
            int j = this.hybridTransitions[i];
            enabled[j] = guard[j].evaluate() || guard[j].changeContinuously();
        }
        for (int i=0;i<this.numberOfStochasticTransitions;i++) {
            int j = this.stochasticTransitions[i];
            enabled[j] = guard[j].evaluate() || guard[j].changeContinuously();
        }
        this.evaluatedTransitions = new HashSet<Integer>();
        for (int i=0;i<this.numberOfContinuousTransitions;i++) {
            int j = this.continuousTransitions[i];
            this.evaluatedTransitions.add(j);
        }
        for (int i=0;i<this.numberOfHybridTransitions;i++) {
            int j = this.hybridTransitions[i];
            this.evaluatedTransitions.add(j);
        }
        for (int i=0;i<this.numberOfStochasticTransitions;i++) {
            int j = this.stochasticTransitions[i];
            this.evaluatedTransitions.add(j);
        }
    }

    public void updateGuardStatus(int firedTransition, double t, double[] y) {
        Integer[] depGuards = model.getListOfUpdatedGuards(firedTransition);
        vars.setValuesReference(y);
        for (Integer i : depGuards)
            if (this.evaluatedTransitions.contains(i))
                 enabled[i] = (useCache ? guard[i].evaluateCache(vars) : guard[i].evaluate(vars)) 
                         || guard[i].changeContinuously(vars);
    }
    
    
    public void computeDerivatives(double t, double[] y, double[] yDot) throws DerivativeException {
        //resets derivatives to zero
        java.util.Arrays.fill(yDot, 0.0);
        //assign correct value to variables
        vars.setValuesReference(y);
        for (int i=0;i<this.numberOfContinuousTransitions;i++) {
            int j = this.continuousTransitions[i];
            if (enabled[j]) {
                boolean guardStatus = useCache ? guard[j].evaluateCache(vars) : guard[j].evaluate(vars);
                if (guardStatus) {
                    double rateValue = useCache ? rate[j].computeCache(vars) : rate[j].compute(vars);
                    for (Stoichiometry s : stoich[j])
                        yDot[s.variable] += s.coefficient * rateValue;
                }
            }
        }
        for (int i=0;i<this.numberOfHybridTransitions;i++) {
            int j = this.hybridTransitions[i];
            if (enabled[j]) {
                boolean guardStatus = useCache ? guard[j].evaluateCache(vars) : guard[j].evaluate(vars);
                if (guardStatus) {
                    double rateValue = useCache ? rate[j].computeCache(vars) : rate[j].compute(vars);
                    if (this.continuityStatus[j]) {
                        for (Stoichiometry s : stoich[j])
                            yDot[s.variable] += s.coefficient * rateValue;
                    } else {
                        yDot[this.transitionToVariable[j]] = - rateValue;
                    }
                }
            }
        }
        for (int i=0;i<this.numberOfStochasticTransitions;i++) {
            int j = this.stochasticTransitions[i];
            //stochastic transitions can be guarded, no matter what
            if (enabled[j]) {
                boolean guardStatus = useCache ? guard[j].evaluateCache(vars) : guard[j].evaluate(vars);
                if (guardStatus) {
                    double rateValue = useCache ? rate[j].computeCache(vars) : rate[j].compute(vars);
                    yDot[this.transitionToVariable[j]] = - rateValue;
                }
            }
        }
    }

}
